Sql_tuning 실무사례 :: 오라클 실습[SSISO Community]
 
SSISO 카페 SSISO Source SSISO 구직 SSISO 쇼핑몰 SSISO 맛집
추천검색어 : JUnit   Log4j   ajax   spring   struts   struts-config.xml   Synchronized   책정보   Ajax 마스터하기   우측부분

오라클 실습
[1]
등록일:2009-05-28 00:21:45 (0%)
작성자:
제목:Sql_tuning 실무사례
오라클  사용자를  위한


SQL  튜닝  실무  사례

(  PART  III  )

2002년  12월  30일

  
PART    III
퀴즈  1.    중복된  로우만  골라서  삭제하라.  4
퀴즈  2.    정확히  10위까지만  구하라.  6
퀴즈  3.    공수별  비용  집계를  구하라.  11
퀴즈  4.    제품별  집계와  등급별  집계를  같이  구하라.  13
퀴즈  5.    당일  분,  일주일  분,  한달  분을  같이  구하라.  15
퀴즈  6.    한  테이블의  값을  가로로  두  번  표시하라.  17
퀴즈  7.    값이  바뀔  때  마다  일련번호를  다시  시작하라.  21
퀴즈  8.    불용  카드  수를  구하라.  26
퀴즈  9.    판매일자,  판매량  별로  누적  판매량을  구하라.  29
퀴즈  10.    4/4  분기  동안  점포별    매출액을  구하라.  33
퀴즈  11.    특정  품종에  대한  총  판매수량과  금액을  구하라.  36
퀴즈  12.    한번이라도  매출을  일으킨  고객을  구하라.  37
퀴즈  13.    매출액  20,000  이하  또는  없는  고객을  삭제하라.  38
퀴즈  14.    가산점과  신용  한도를  증가시켜라.  39
퀴즈  15.    매출  순위  10위까지의  고객  리스트를  작성하라.  40
퀴즈  16.    일별  총계  및  제품별  총계를  같이  구하라.  43
퀴즈  17.    입력  구분에  따라  서로  다른  총매출액을  구하라.  45
퀴즈  18.    년간  매출을  주  단위로  매출  구분하여  구하라  46





여기서  소개된  퀴즈들은  독자들의  SQL  이해력을  높이고  집합적  사고  능력을  함양  시키고자  준비한  것으로  간단하지만  많은  생각을  하여야만  정확한  답을  구할  수가  있다.  
다만,  주의  할  점은  데이터가  많아졌을  때  나타날  수  있는  성능  상의  문제는  고려하지  않았으므로  실무  적용  시에는  다른  여러  가지  요소를  같이  고려하여  적용해야  한다는  것이다.

  
퀴즈  1.    중복된  로우만  골라서  삭제하라.

1)  배경

프로그램  개발을  위하여  샘플  데이터를  로딩한  뒤  PRIMARY  KEY나  UNIQUE  인덱스를  만들고  싶지만  원시  데이터의  잘못으로  중복된  로우가  있으면  에러가  발생한다.
이럴  경우에  이를  확인하고  삭제하고  싶을  때가  있다.

EMP  테이블을  예를  들어  설명한다.

SQL>  SELECT  ROWID,  ROWNUM,  EMPNO,  ENAME
    2    FROM  EMP
    3    ORDER  BY  EMPNO,  ROWID;

ROWID                              ROWNUM      EMPNO  ENAME
------------------  -------  -------  ----------
00024F04.0002.0005              3        7698  BLAKE
00024F04.0005.0005              4        7698  BLAKE      <--  삭제  대상
00024F04.0006.0005              5        7782  CLARK
00024F04.0001.0005              2        7788  SCOTT
00024F04.0007.0005              6        7788  SCOTT      <--  삭제  대상
00024F04.000B.0005            10        7788  SCOTT      <--  삭제  대상
00024F04.0008.0005              7        7839  KING
00024F04.0000.0005              1        7844  TURNER
00024F04.0009.0005              8        7844  TURNER    <--  삭제  대상
00024F04.000A.0005              9        7876  ADAMS

10  rows  selected.

2)  요구  내용

위의  데이터  중  EMPNO  컬럼의  값이  중복된  로우에  대하여  ROWID  값이  큰  로우만  골라서  삭제하라.

3)  해답

중복된  로우를  찾아보기만  하는  경우

SELECT    EMPNO,  ENAME    FROM  EMP  A
WHERE      ROWID    >    (  SELECT  MIN(ROWID)  FROM  EMP  B
                                          WHERE  A.EMPNO  =  B.EMPNO  );

중복된  로우를  찾아서  ROWID가  더  큰  것을  삭제하는  경우  이  때  ROWID가  더  큰  것은  아마도  뒤에  생긴  데이터일  가능성이  더  많기  때문이다.

DELETE  FROM  EMP  A
WHERE    ROWID    >    (  SELECT  MIN(ROWID)  FROM  EMP  B
                                      WHERE  A.EMPNO  =  B.EMPNO  );

만약  나중에  들어온  데이터를  살리고  먼저  들어온  데이터를  삭제하고자  한다면  WHERE  조건의  부등호와  MIN을  MAX로  바꾸어서  다음과  같이  하면  된다.

DELETE  FROM  EMP  A
WHERE    ROWID    <    (  SELECT  MAX(ROWID)  FROM  EMP  B
                                      WHERE  A.EMPNO  =  B.EMPNO  );


  
퀴즈  2.    정확히  10위까지만  구하라.

1)  배경

고정된  형태의  화면에서  항상  정확히  10  등까지의  데이터만  필요한  경우이다.
다음과  같은  EMP  테이블과  데이터가  있다.

SELECT  ROWNUM,  SAL,  ENAME,  JOB,  EMPNO
FROM  EMP
ORDER  BY  SAL  DESC,  ENAME;

ROWNUM  데이터는  ORDER  BY에  의해  소트  가  수행되기  전에  붙여지므로  결과는  다음과  같이  나타난다.

  ROWNUM          SAL  ENAME            JOB                  EMPNO
-------  -------  ----------  ---------  -------
            9        5000  KING              PRESIDENT        7839
          13        3000  FORD              ANALYST            7902
            8        3000  SCOTT            ANALYST            7788
            4        2975  JONES            MANAGER            7566
            6        2850  BLAKE            MANAGER            7698
            7        2450  CLARK            MANAGER            7782
            2        1600  ALLEN            SALESMAN          7499
          10        1500  TURNER          SALESMAN          7844
          14        1300  MILLER          CLERK                7934    <---    9  번째  로우    9  위
            5        1250  MARTIN          SALESMAN          7654    <---  10  번째  로우  10  위    :  CASE  2
            3        1250  WARD              SALESMAN          7521    <---  11  번째  로우  10  위    :  CASE  1
          11        1100  ADAMS            CLERK                7876    <---  12  번째  로우  12  위
          12          950  JAMES            CLERK                7900
            1          800  SMITH            CLERK                7369

14  rows  selected.


2)  요구  내용

위의  데이터를  이용하여  순위를  나타내는  컬럼과  함께  10위  까지  나오는  SQL문과  (위의  샘플  데이터  경우  WARD,  11  번째  로우)  정확히  10번째  로우까지만  나오는  (위의  샘플  데이터  경우  MARTIN,  10  번째  로우)  SQL문을  작성하라.
즉,  다음과  같은  결과가  나오게  하라.

  ROWNUM          SAL  ENAME            JOB                  EMPNO
-------  -------  ----------  ---------  -------
            1        5000  KING              PRESIDENT        7839
            2        3000  FORD              ANALYST            7902
            3        3000  SCOTT            ANALYST            7788
            4        2975  JONES            MANAGER            7566
            5        2850  BLAKE            MANAGER            7698
            6        2450  CLARK            MANAGER            7782
            7        1600  ALLEN            SALESMAN          7499
            8        1500  TURNER          SALESMAN          7844
            9        1300  MILLER          CLERK                7934
          10        1250  MARTIN          SALESMAN          7654
          11        1250  WARD              SALESMAN          7521

11  rows  selected.

3)  해답

&#61548;  CASE  1  :  상위  10위  까지  의  로우를  출력

SELECT    ROWNUM,  SAL,  ENAME,  JOB,  EMPNO
FROM        EMP  E1
WHERE      10  >  (  SELECT  COUNT(*)
                              FROM  EMP  E2
                              WHERE  E2.SAL  >  E1.SAL  )
ORDER    BY    SAL  DESC  ;
  
다음과  같은  출력이  나온다.

  ROWNUM          SAL  ENAME            JOB                  EMPNO
-------  -------  ----------  ---------  -------
            8        5000  KING              PRESIDENT        7839
            7        3000  SCOTT            ANALYST            7788
          10        3000  FORD              ANALYST            7902
            3        2975  JONES            MANAGER            7566
            5        2850  BLAKE            MANAGER            7698
            6        2450  CLARK            MANAGER            7782
            1        1600  ALLEN            SALESMAN          7499
            9        1500  TURNER          SALESMAN          7844
          11        1300  MILLER          CLERK                7934
            2        1250  WARD              SALESMAN          7521
            4        1250  MARTIN          SALESMAN          7654

11  rows  selected.

그러나  이  출력은  ROWNUM  값이  소트  되기  전에  붙으므로  원하는  결과와  다르다.
ROWNUM을  순차적으로  표시하기  위해  IN-LINE  VIEW를  하나  더  씌운다.
그러나  이렇게  되면  IN-LINE  VIEW  내에서  ORDER  BY  문을  사용할  수  없게  되므로  GROUP  BY  문을  활용하여  소트  기능을  대신한다.

SELECT  ROWNUM    AS  RANK,    -SAL  SAL,  ENAME,  JOB,  EMPNO
FROM  (  SELECT  -SAL  SAL,  ENAME,  JOB,  EMPNO
              FROM      EMP  E1
              WHERE    10    >    (  SELECT  COUNT(*)
                                              FROM  EMP  E2
                                              WHERE  E2.SAL  >  E1.SAL  )
              GROUP  BY  -SAL,  ENAME,  JOB,  EMPNO)  ;

다음과  같은  출력이  나온다.




ROWNUM          SAL  ENAME            JOB                  EMPNO
-------  -------  ----------  ---------  -------
            1        5000  KING              PRESIDENT        7839
            2        3000  SCOTT            ANALYST            7788
            3        3000  FORD              ANALYST            7902
            4        2975  JONES            MANAGER            7566
            5        2850  BLAKE            MANAGER            7698
            6        2450  CLARK            MANAGER            7782
            7        1600  ALLEN            SALESMAN          7499
            8        1500  TURNER          SALESMAN          7844
            9        1300  MILLER          CLERK                7934
          10        1250  WARD              SALESMAN          7521
          11        1250  MARTIN          SALESMAN          7654

11  rows  selected.

&#61548;  CASE  2  :  상위  10째  까지  의  로우를  출력

SELECT  ROWNUM    AS  RANK,    -SAL  SAL,  ENAME,  JOB,  EMPNO
FROM  (  SELECT  -SAL  SAL,  ENAME,  JOB,  EMPNO
              FROM      EMP  E1
              WHERE    10    >    (  SELECT  COUNT(*)
                                              FROM  EMP  E2
                                              WHERE  E2.SAL  >  E1.SAL  )
              GROUP  BY  -SAL,  ENAME,  JOB,  EMPNO)  
WHERE  ROWNUM  <=  10  ;

최종  결과는  위와  같다.
정확히  10  번째  로우에서  자르기  위해  ROWNUM  <=  10  조건을  추가하였다.

그러나  처음부터  10  번째  로우에서  자르겠다면  굳이  위와  같이  하지  않고  간단히  아래와  같이  하는  방법도  있다.


SELECT    ROWNUM,  -SAL  SAL,  ENAME,  JOB,  EMPNO
FROM      (SELECT  -SAL  SAL,  ENAME,  JOB,  EMPNO
                FROM      EMP
                GROUP  BY  -SAL,  ENAME,  JOB,  EMPNO)
WHERE  ROWNUM  <=  10  ;

위의  두  SQL문의  결과는  다음과  같다.

ROWNUM          SAL    ENAME            JOB                  EMPNO
-------  -------  ----------  ---------  -------
            1        5000  KING              PRESIDENT        7839
            2        3000  SCOTT            ANALYST            7788
            3        3000  FORD              ANALYST            7902
            4        2975  JONES            MANAGER            7566
            5        2850  BLAKE            MANAGER            7698
            6        2450  CLARK            MANAGER            7782
            7        1600  ALLEN            SALESMAN          7499
            8        1500  TURNER          SALESMAN          7844
            9        1300  MILLER          CLERK                7934
          10        1250  MARTIN          SALESMAN          7654

10  rows  selected.

  
퀴즈  3.    공수별  비용  집계를  구하라.

1)  배경

어떤  건설  회사의  전표  테이블에서  투입된  공임에  대한  데이터를  다음과  같이  관리하고  있다.
동일한  성격의  일을  하고  있는데  작업  번호  101    번은  1  명이  투입되어  6  시간  걸려서  끝냈고  그때의  비용은  200,000  원  이었고,  102  번은  3  명이  투입되어  2  시간  만에  끝냈으며  그  때의  비용은  230,000원  이다.

작업  번호  인원  시간  비용
101  1  6  200,000
102  3  2  230,000
103  2  3  220,000
104  6  1  300,000
105  1  8  300,000
106  5  3  500,000
107  2  6  400,000
108  1  15  550,000
109  1  12  450,000
110  3  5  530,000
...  ...  ...
150  4  6  900,000

2)  요구  내용

작업  번호  101,  102번을  예로  들면  이  두  작업은  서로  다른  것  같지만  "인원*시간"  을  한  값이  같으므로  같은  공수로  볼  수  있다.    
이와  같은  맥락으로  보면  작업  번호  103,  104번도  동일한  공수이다.
따라서  공수가  6인  작업의  비용을  모두  구하면  200,000  +  230,000  +  220,000  +  300,000  =  950,000원이  된다.

공수  비용합계  요구  내용
6  950,000  왼편의  표와  같이  모든  공수의  종류를  구하고  그  공수별로  들어간  비용의  합계를  구하라.

8  300,000  
12  850,000  
15  1,580,000  
...  .....  
24  900,000  


3)  해답

SELECT  시간*인원  "공수",  SUM(비용)  "금액"
FROM      전표테이블
WHERE    발생일  LIKE  '9610%'
GROUP  BY  시간*인원  ;

GROUP  BY  시간*인원  라는  아이디어를  이용하여  하나의  SQL  문으로  풀  수  있다.

  
퀴즈  4.    제품별  집계와  등급별  집계를  같이  구하라.

1)  배경

어떤  제조  회사의  제품별  판매  내용이  다음과  같다.
1996년  10월에  판매된  제품의  내용은  다음과  같다.
  
제품명  모델명  수량  매출액  집계구분
HP  HJ300  1  10,000  제




HP  HJ300  2  20,000  
HP  HA120  1  5,000  
HP  HB580  1  20,000  
HP  HB580  3  6,000  
LD  A530G  1  100,000  모



용  





LD  A530G  3  300,000  
LD  A210K  2  400,000  
LD  A210K  3  600,000  
PP  P530C  10  100,000  
PP  P530D  5  25,000  기
타  
PP  R640A  10  15,000  
PP  R650B  20  20,000  

2)  요구  내용

위와  같이  판매일자와  함께  각각의  제품에  대한  판매  내용이  있을  때  전체  판매에  대한  수량과  매출액  등의  통계자료를  보고싶은  것이다.
단,  통계자료를  볼  때  제품명이  HP인  제품은  저가여서  제품별로  만  봐도  되니까  모델  별은  무시하고  제품별로  만  수량과  매출액  합계를  내고,  제품명이  LD인  제품은  고가이므로  각  모델별로  수량과  매출액  합계를  내고,  제품명이  PP인  제품은  저가  단품들  이므로  모든  모델을  하나로  몰아서  기타로  하여  수량과  매출액에  대한  합계를  구하라.
그러나  제품명이  PP인  제품  중에서  모델명이  P530C인  제품은  최근에  개발한  신제품이므로  기타에  합치지  말고  별도로  빼내어  모델명까지  구분하도록  하라.

3)  해답

GROUP  BY  와  DECODE  의  절묘한  조화로  다음과  같이  하나의  SQL  문으로  답을  구할  수  있다.

SELECT  DECODE(제품,'HP',  제품,  'LD',  모델명,  'PP',
                                        DECOE(모델명,  'P530C',  모델명,  '기타')),    <---  제품이  'PP'인  경우
              SUM(수량),  SUM(매출액)
FROM      매출테이블
WHERE    매충일  LIKE  '199610%'
GROUP    BY    DECODE(제품,'HP',  제품,  'LD',  모델명,  'PP',
                                                DECODE(모델명,  'P530C',  모델명,  '기타'))  ;





  
퀴즈  5.    당일  분,  일주일  분,  한달  분을  같이  구하라.

1)  배경

다음과  같은  판매  데이터가  들어있는  테이블이  있다.
온라인  화면에서  사용되며  통계  테이블은  별도로  준비되어  있지  않다.

SALEDATE
SALEDEPT  ITEM  CHULQTY  SALEAMT
...  ...  ...  ...  ...
19961021  100  P100  100  1000
19961021  100  P102  50  500
19961021  101  Q110  10  2000
19961021  101  Q111  10  2000
19961021  102  R110  20  1000
19961022  100  P100  200  2000  최근  최고분
19961022  100  P102  100  1000  
19961022  101  Q110  5  1000  최근  최고분
...  ...  ...  ...  ...  

2)  요구  내용

위  테이블을  이용하여  당일분  합계,  최근  일주일분  합계,  가장  최근의  최고  판매량분,  월간  합계를  같이  구하라.

통상  "  가장  최근의  최고  판매량분"  은  그날의  판매  데이터  중  최고  판매량  이지만  그날의  데이터가  아직  들어가지  않은  상태라면  어제의  판매량  중  최고의  데이터를  가져온다.  
만약  오늘이  1996년  10월  22일  이라면  부서별  가장  최근  및  최고  분은  위의  표시된  로우와  같다.




3)  해답

DECODE,  SIGN,  MAX  함수를  사용하여  다음과  같이  하나의  SQL  문으로  해결할  수  있다.

SELECT  SALEDEPT,  
              SUM(DECODE(SALEDATE,  TO_CHAR(SYSDATE,'YYYYMMDD'),SALEQTY,0))  AS  '당일합계'  ,
              SUM(DECODE(SIGN  (8  -  (SYSDATE  -  TO_DATE(SALEDATE,'YYYYMMDD'))  
                                                      ),  1,  SALEQTY))  AS    '일주일분합계'  ,  
              SUBSTR(MAX(SALEDATE  ||  CHULQTY),9,20)  AS  '최근  최고분'
              SUM(SALEAMT)  AS  '월간합계'
FROM      판매테이블
WHERE    SALEDATE  LIKE  '9610%'
GROUP    BY  SALEDEPT  ;






  
퀴즈  6.    한  테이블의  값을  가로로  두  번  표시하라.

1)  배경

일반  프로그램이나  웹  브라우저에서  동일한  테이블의  데이터를  옆으로  반복하여  표시하고  할  때  사용할  수  있다.
다음과  같은  데이터가  있다고  본다.

SQL>  SELECT  EMPNO,ENAME  FROM  EMP  ;

EMPNO  ENAME
-------  ----------
      7369  SMITH
      7499  ALLEN
      7521  WARD
      7566  JONES
      7654  MARTIN
      7698  BLAKE
      7782  CLARK
      7788  SCOTT
      7839  KING
      7844  TURNER
      7876  ADAMS

11  rows  selected.

2)  요구  내용

위와  같은  결과가  다음과  같이  한  테이블의  값을  가로로  두  번  표시하라.





        ORD      EMPNO  ENAME            JOB                  EMPNO  ENAME            JOB
-------  -------  ----------  ---------  -------  ----------  ---------
            1        7369  SMITH            CLERK                7499  ALLEN            SALESMAN
            2        7521  WARD              SALESMAN          7566  JONES            MANAGER
            3        7654  MARTIN          SALESMAN          7698  BLAKE            MANAGER
            4        7782  CLARK            MANAGER            7788  SCOTT            ANALYST
            5        7839  KING              PRESIDENT        7844  TURNER          SALESMAN
            6        7876  ADAMS            CLERK

6  rows  selected.

3)  해답

STEP  1  :  우선  ROWNUM을  짝수,  홀수로  구분하여  가로로  표시한다.

SQL>  SELECT  ROWNUM,TRUNC((ROWNUM+1)/2)  AS  ORD,
    2                  DECODE(MOD(ROWNUM,2),1,EMPNO,NULL)  AS  FIRST_EMPNO,
    3                  DECODE(MOD(ROWNUM,2),0,EMPNO,NULL)  AS  SECOND_EMPNO
    4    FROM      EMP  ;

  ROWNUM          ORD  FIRST_EMPNO  SECOND_EMPNO
-------  -------  -----------  ------------
            1              1                7369
            2              1                                          7499
            3              2                7521
            4              2                                          7566
            5              3                7654
            6              3                                          7698
            7              4                7782
            8              4                                          7788
            9              5                7839
          10              5                                          7844
          11              6                7876

11  rows  selected.

STEP  2  :  빈  컬럼을  없애기  위해  SUM(DECODE...)  를  사용한다.

SQL>  SELECT  TRUNC((ROWNUM+1)/2)  AS  ORD,
    2                  SUM(DECODE(MOD(ROWNUM,2),1,EMPNO,NULL))  AS  FIRST_EMPNO,
    3                  SUM(DECODE(MOD(ROWNUM,2),0,EMPNO,NULL))  AS  SECOND_EMPNO
    4    FROM      EMP
    5    GROUP  BY  TRUNC((ROWNUM+1)/2)  ;

        ORD  FIRST_EMPNO  SECOND_EMPNO
-------  -----------  ------------
            1                7369                  7499
            2                7521                  7566
            3                7654                  7698
            4                7782                  7788
            5                7839                  7844
            6                7876

6  rows  selected.

STEP  3  :  해당  EMPNO  컬럼에  ENAME,  JOB  데이터를  덧붙인다.

SQL>  SELECT  ORD,
    2                  F_EMPNO  AS  EMPNO,A.ENAME,A.JOB,
    3                  S_EMPNO  AS  EMPNO,B.ENAME,B.JOB
    4    FROM      (  SELECT  ORD,
    5                                    SUM(FIRST_EMPNO)    AS  F_EMPNO,
    6                                    SUM(SECOND_EMPNO)  AS  S_EMPNO
    7                      FROM      (  SELECT  TRUNC((ROWNUM+1)/2)  AS  ORD,
    8                                                      DECODE(MOD(ROWNUM,2),1,EMPNO,NULL)  AS  FIRST_EMPNO,
    9                                                      DECODE(MOD(ROWNUM,2),0,EMPNO,NULL)  AS  SECOND_EMPNO
  10                                        FROM  EMP)
  11                      GROUP  BY  ORD  ),  EMP  A,  EMP  B
  12    WHERE    F_EMPNO  =  A.EMPNO
  13    AND        S_EMPNO  =  B.EMPNO(+)
  14    ORDER  BY  ORD  ;

OUTER  조인을  사용하지  않으면  SECOND_EMPNO  컬럼의  값이  NULL인  경우  전체  로우의  값이  나오지  않게  되므로  반드시  S_EMPNO와  조인되는  B.EMPNO  컬럼에  (+)  사인을  추가해야  한다.

        ORD      EMPNO  ENAME            JOB                  EMPNO  ENAME            JOB
-------  -------  ----------  ---------  -------  ----------  ---------
            1        7369  SMITH            CLERK                7499  ALLEN            SALESMAN
            2        7521  WARD              SALESMAN          7566  JONES            MANAGER
            3        7654  MARTIN          SALESMAN          7698  BLAKE            MANAGER
            4        7782  CLARK            MANAGER            7788  SCOTT            ANALYST
            5        7839  KING              PRESIDENT        7844  TURNER          SALESMAN
            6        7876  ADAMS            CLERK

6  rows  selected.
  
퀴즈  7.    값이  바뀔  때  마다  일련번호를  다시  시작하라.

1)  배경

다음과  같은  데이터가  들어있는  테이블이  있다.
이해를  돕기  위해  DUMMY  컬럼을  넣어서  작성하였지만  실제로는  DUMMY  컬럼은  없다.
참고로  ROWNUM  컬럼도  같이  보기  바란다.

SQL>  DESC  TSTBL;

  Name                                                        Null?        Type
  -------------------------------  --------  ----
  PK1                                                                            CHAR(3)
  PK2                                                                            CHAR(5)
  DUMMY                                                                        NUMBER

SQL>  SELECT  PK1,  PK2,  DUMMY,  ROWNUM  FROM  TSTBL
    2    ORDER  BY  PK1,  PK2  ;

PK1  PK2          DUMMY    ROWNUM
---  -----  -------  -------
A01  AS011              1              1
A01  AS012              2              2
A01  AS013              3              3
A01  AS014              4              4
A01  AS015              5              5
A02  AS021              1              6
A02  AS022              2              7
A02  AS023              3              8
A03  AS021              1              9
B01  BS011              1            10
B01  BS012              2            11
B01  BS013              3            12
B01  BS014              4            13
B01  BS015              5            14
B01  BS016              6            15
B01  BS017              7            16
B01  BS018              8            17  
B01  BS018              8            17
B02  BS021              1            18
B02  BS022              2            19
B02  BS023              3            20
B02  BS024              4            21
C01  CS011              1            22
C01  CS012              2            23
C02  CS021              1            24
C02  CS022              2            25
C02  CS023              3            26
C02  CS024              4            27

27  rows  selected.

2)  요구  내용

위의  데이터  중  PK1,  PK2  컬럼만  사용하여  PK1  컬럼의  값이  바뀔  때  마다  일련번호가  1  부터  새롭게  시작되도록  하라.
즉,  일련번호가  나오는  컬럼을  SEQ  컬럼이라  하면  PK1,  PK2  컬럼만  사용하여  SEQ  컬럼이  값이  DUMMY  컬럼의  값처럼  나오게  하라.

3)  해답

우선  기존에  나오는  ROWNUM  컬럼의  값과  PK1으로  GROUP  BY하여  나오는  COUNT(*)  값을  이용하여  구한다.



STEP  1  :  우선  GROUP  BY  한  값을  COUNT  한  뒤  ROWNUM을  붙여서  이  결과를  SELF  조인  하여  바로  전  PK1  값  까지  의  COUNT  값을  구한다.  

SQL>  SELECT  B.PK1,  B.RNUM  AS  B_RNUM,  C.RNUM  AS  C_RNUM,  C.CNT
    2    FROM      (SELECT  ROWNUM  AS  RNUM,  PK1,  CNT
    3                    FROM      (SELECT  PK1,  COUNT(*)    AS  CNT    FROM      TSTBL
    4                                    GROUP  BY  PK1))  B,
    5                  (SELECT  ROWNUM  AS  RNUM,  PK1,  CNT
    6                    FROM      (SELECT  PK1,  COUNT(*)    AS  CNT    FROM      TSTBL
    7                                    GROUP  BY  PK1))  C
    8    WHERE    B.RNUM  >  C.RNUM  ;

PK1    B_RNUM    C_RNUM          CNT
---  -------  -------  -------
A02              2              1              5    <--  A01  값의  COUNT(*)    <--  A02
A03              3              1              5    <--  A01  값의  COUNT(*)    <--  A03
A03              3              2              3    <--  A02  값의  COUNT(*)
B01              4              1              5    <--  A01  값의  COUNT(*)    <--  B01
B01              4              2              3    <--  A02  값의  COUNT(*)
B01              4              3              1    <--  A03  값의  COUNT(*)
B02              5              1              5    <--  A01  값의  COUNT(*)    <--  B02
B02              5              2              3    <--  A02  값의  COUNT(*)
B02              5              3              1    <--  A03  값의  COUNT(*)
B02              5              4              8    <--  B01  값의  COUNT(*)
C01              6              1              5    <--  A01  값의  COUNT(*)    <--  C01
C01              6              2              3    <--  A02  값의  COUNT(*)
C01              6              3              1    <--  A03  값의  COUNT(*)
C01              6              4              8    <--  B01  값의  COUNT(*)
C01              6              5              4    <--  B02  값의  COUNT(*)
C02              7              1              5    <--  A01  값의  COUNT(*)    <--  C02
C02              7              2              3    <--  A02  값의  COUNT(*)
C02              7              3              1    <--  A03  값의  COUNT(*)
C02              7              4              8    <--  B01  값의  COUNT(*)
C02              7              5              4    <--  B02  값의  COUNT(*)
C02              7              6              2    <--  C01  값의  COUNT(*)



STEP  2  :  바로  이전  PK1  값까지의  누계  COUNT  값을  알기  위해  PK1  값과  B.RNUM  값으로  GROUP  BY  한다.

SQL>  SELECT  B.PK1,  B.RNUM,  SUM(C.CNT)  AS  ACC_SUM
    2    FROM      (SELECT  ROWNUM  AS  RNUM,  PK1,  CNT
    3                    FROM      (SELECT  PK1,COUNT(*)  AS  CNT    FROM      TSTBL
    4                                    GROUP  BY  PK1))  B,
    5                  (SELECT  ROWNUM  AS  RNUM,  PK1,  CNT
    6                    FROM      (SELECT  PK1,COUNT(*)  AS  CNT    FROM      TSTBL
    7                                    GROUP  BY  PK1))  C
    8    WHERE    B.RNUM  >  C.RNUM
    9    GROUP    BY    B.PK1,  B.RNUM  ;

PK1        RNUM  ACC_SUM
---  -------  -------
A02              2              5
A03              3              8
B01              4              9
B02              5            17
C01              6            21
C02              7            23

6  rows  selected.



STEP  3  :  바로  이전  PK1  값까지의  누계  값과  원래의  ROWNUM을  이용하여  최종결과를  구한다.    이때  OUTER  조인을  사용해야만  "PK1  =  A01"  값이  빠지지  않게  된다.

SQL>  SELECT  A.PK1,  A.RNUM  -  NVL(ACC_SUM,0)  AS  SEQ,  A.PK2,  A.DUMMY
    2    FROM      (SELECT  PK1,ROWNUM  AS  RNUM,PK2,DUMMY
    3                    FROM      (SELECT  PK1,PK2,DUMMY
    4                                    FROM      TSTBL
    5                                    GROUP  BY  PK1,PK2,DUMMY))  A,
    6                  (SELECT  B.PK1,B.RNUM,SUM(C.CNT)  AS  ACC_SUM
    7                    FROM      (SELECT  ROWNUM  AS  RNUM,  PK1,CNT
    8                                    FROM      (SELECT  PK1,COUNT(*)  AS  CNT    FROM      TSTBL
    9                                                    GROUP  BY  PK1))  B,
  10                                  (SELECT  ROWNUM  AS  RNUM,  PK1,CNT
  11                                    FROM      (SELECT  PK1,COUNT(*)  AS  CNT    FROM      TSTBL
  12                                                    GROUP  BY  PK1))  C
  13                    WHERE    B.RNUM  >  C.RNUM
  14                    GROUP  BY  B.PK1,B.RNUM)  D
  15    WHERE    A.PK1  =  D.PK1(+)
  16    ORDER    BY  A.PK1,  A.RNUM  -  NVL(ACC_SUM,0)  ;

실행  결과는  다음과  같으며  SEQ  컬럼은  PK1  값이  바뀌면  일련번호를  다시  시작하는  것을  볼  수  있다.
DUMMY  컬럼은  SEQ  값이  정확히  잘  나왔는가를  검토하는  목적으로  추가된  것이고  실제의  테이블에는  없다.
또한  SQL  컬럼도  IN-LINE  VIEW  내의  PSEUDO  컬럼인  ROWNUM  컬럼에서  나왔으므로  실제  테이블에는  없다.




PK1          SEQ  PK2          DUMMY
---  -------  -----  -------
A01              1  AS011              1
A01              2  AS012              2
A01              3  AS013              3
A01              4  AS014              4
A01              5  AS015              5
A02              1  AS021              1
A02              2  AS022              2
A02              3  AS023              3
A03              1  AS021              1
B01              1  BS011              1
B01              2  BS012              2
B01              3  BS013              3
B01              4  BS014              4
B01              5  BS015              5
B01              6  BS016              6
B01              7  BS017              7
B01              8  BS018              8
B02              1  BS021              1
B02              2  BS022              2
B02              3  BS023              3
B02              4  BS024              4
C01              1  CS011              1
C01              2  CS012              2
C02              1  CS021              1
C02              2  CS022              2
C02              3  CS023              3
C02              4  CS024              4

27  rows  selected.






  
퀴즈  8.    불용  카드  수를  구하라.

1)  배경

카드  사에서  본인  카드와  그것의  가족  카드를  연관하여  일정  기간  사용되지  않은  카드  수를  계산한다.  
다음과  같은  샘플  데이터로  단순화  시켜서  카드  수를  구한다.

카드정보  테이블  카드사용실적  테이블
카드번호
본인카드번호  카드번호  사용금액
101  NULL  101              10,000
102  101  101                5,500
103  101  105              35,000
104  NULL  07              70,000
105  NULL  
106  NULL  
107  104  
108  104  
109  NULL  
110  NULL  

2)  요구  내용

위  샘플  데이터로부터  보면  사용  실적이  없는  카드  번호의  단순  리스트는    102,  103,  104,  106,  108,  109,  110등  모두  7개  카드지만  여기서  문제는  102,  103  카드는  이것의  본인  카드인  101이  사용  실적이  있으므로  실제적으로  답에서  빠져야  하며  104  카드도  역시  해당  가족  카드인  107이  사용  실적이  있으므로  104,  108도  역시  답에서  빠져야  하기  때문에  결과적으로  사용  실적이  없는  카드의  답은  106,  109,  110  모두  3개의  카드가  된다.  
이를  구하는  쿼리를  작성하라.




3)  해답

    SELECT    카드번호    
        FROM    카드정보    A
      WHERE    NOT  EXISTS    (SELECT  X  FROM  카드정보  B
                                              WHERE    EXISTS  (  SELECT  X  FROM  카드사용실적  C
                                                                                WHERE    C.카드번호  =  B.카드번호)
                                              START  WITH    B.카드번호  =  NVL(A.본인카드번호,  A.카드번호)
                                              CONNECT  BY  PRIOR    B.카드번호  =  B.본인카드번호)  ;

위에서  START  WITH  B.카드번호  =  NVL(A.본인카드번호,  A.카드번호)  절은  연관  카드를  찾아서  수행되도록  하여  요구  사항의  누락이  없도록  본인  카드로  부터  시작하여  카드  사용  실적을  찾도록  하기  위함이며  START  WITH  ~  CONNECT  BY  PRIOR은  이책의  PART  II  사례  23번,  26번을  참조하여  이해를  구하거나  오라클  "Sql  Language  Reference  Manual"  Chapter  4  Commands  의  SELECT  부분을  참조하기  바란다.





퀴즈  9.    판매일자,  판매량  별로  누적  판매량을  구하라.

1)  배경

다음과  같은  데이터가  들어있는  테이블이  있다.

SQL>  DESC  BFUN1;

  Name                                                        Null?        Type
  -------------------------------  --------  ----
  판매일자                                                Not  Null  VARCHAR2(8)
  판매량                                                                      NUMBER

SQL>  SELECT  *  FROM  BFUN1;

판매일자    판매량
--------  -------
19970225            10
19970214            40
19970218            23
19970220            65
19970208            43
19970203            22
19970211            18
19970218            15
19970213            60
19970218            19

      10  개의  행이  선택되었습니다.

2)  요구  내용

위의  10개  데이터를  판매일자를  기준으로  소트  하였을  때  그에  해당하는  판매량  까지  구분하여    판매량을  누적한  누적  판매량을  같이  표시하도록  하라.
즉,  다음과  같이  데이터가  나오도록  하라.

판매일자    판매량  누적판매량
--------  -------  ----------
19970203            22                  22
19970208            43                &  
[본문링크] Sql_tuning 실무사례
[1]
코멘트(이글의 트랙백 주소:/cafe/tb_receive.php?no=31516
작성자
비밀번호

 

SSISOCommunity

[이전]

Copyright byCopyright ⓒ2005, SSISO Community All Rights Reserved.